package core.iterfaces;


import core.vec.DenseVec;
import exception.ExceptionUtils;
import exception.NotSupportException;

import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 矩阵的接口
 */
public interface Matrix{
    /**
     * 返回一个和当前矩阵同类型，同长度大小的矩阵
     * @return 返回的矩阵
     */
    Matrix zero();

    /**
     * 返回一个和当前矩阵同类型，长度为rowLen,宽度为colLen的矩阵
     * @param rowLen 长度
     * @param colLen 宽度
     * @return 返回的矩阵
     */
    Matrix zero(int rowLen,int colLen);
    /**
     * 获取矩阵i,j的元素
     * @param i  元素所在行
     * @param j  元素所在列
     * @return 元素值
     */

    double get(int i,int j);


    /**
     * 设置矩阵元素
     * @param i 元素所在行
     * @param j 元素所在列
     * @param value 元素值
     */
    void set(int i,int j,double value);

    /**
     * 以向量形式返回元素的某一行
     * @param i 行号
     * @return 返回所在行对应的向量
     */
    Vec getRow(int i) throws NotSupportException;

    /**
     * 以向量形式返回元素的某一列
     * @param j 列号
     * @return 以向量的形式返回矩阵的列
     */
    Vec getCol(int j);

    /**
     * 以向量的形式返回行的某一部分
     * @param i 行号
     * @param begin 起始位置
     * @param end 结束位置
     * @return 行的某一部分
     */
    Vec getSubRow(int i,int begin,int end) throws NotSupportException;

    /**
     * 以向量的形式返回列的某一部分
     * @param j 列号
     * @param begin 列起始位置
     * @param end 列结束位置
     * @return 列的一部分
     */
    Vec getSubCol(int j,int begin,int end) throws NotSupportException;

    /**
     * 返回矩阵的第k条对角线
     * @param k 对角线的位置 k=0为对角线 k>0 为上对角线 k<1为下对角线
     * @return 以向量形式返回的对角线
     */
    default Vec getKDiag(int k) throws NotSupportException{
        /**
         * 找到行和列中较小的一个数字
         */
        int len = -1;
        DenseVec result = null;
        if(k>=0){
            len = getColLen() -k<getRowLen()?getColLen()-k:getRowLen();
            result =new DenseVec(len);
            for (int i = 0; i < len; i++) {
                result.set(i,get(i,i+k));
            }
        }
        else{
            len = getRowLen()+k<getColLen()?getRowLen()+k:getColLen();
            result =new DenseVec(len);
            for (int i = 0; i < len; i++) {
                result.set(i,get(i+k,i));
            }
        }
        return result;
    }

    /**
     * 获取子矩阵
     * @param rowBegin 子矩阵起始行
     * @param rowEnd  子矩阵结束行
     * @param colBegin 子矩阵起始列
     * @param colEnd 子矩阵结束列
     * @return 子矩阵
     */
    default Matrix sub(int rowBegin,int rowEnd,int colBegin,int colEnd) throws NotSupportException{
        if(rowBegin<0||rowBegin>rowEnd||rowBegin>=getRowLen()){
            throw new NotSupportException("The rowBegin is not in the range!");
        }
        if(rowEnd<rowBegin||rowEnd>getRowLen()){
            throw new NotSupportException("The rowEnd is not in the range!");
        }
        if(colBegin<0||colBegin>colEnd||colBegin>=getColLen()){
            throw new NotSupportException("The colBegin is not in the range!");
        }
        if (colEnd<colBegin||colEnd>getColLen()){
            throw new NotSupportException("The colEnd is not in the range!");
        }
        Matrix result = zero(rowEnd-rowBegin,colEnd-colBegin);
        Stream.iterate(0,item->item+1).limit(result.getRowLen()).parallel().forEach(row->
            Stream.iterate(0,item->item+1).limit(colEnd-colBegin).parallel().forEach(col->
                    result.set(row,col,get(row-rowBegin,col-colBegin))
            )
        );
        return result;
    }

    /**
     * 获取矩阵行数
     * @return 矩阵行数
     */
    int getRowLen();

    /**
     * 获取矩阵列数目
     * @return 矩阵列数
     */
    int getColLen();

    /**
     * 获取矩阵的转置矩阵
     * @return 转置矩阵
     */
    default Matrix transform() {
        Matrix result = zero(getColLen(),getRowLen());
        Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().forEach(row->
            Stream.iterate(0,item->item+1).limit(getColLen()).parallel().forEach(col->
                    result.set(col,row,get(row,col))
            )
        );
        return result;
    }

    /**
     * 矩阵与标量相乘
     * @param a 标量
     * @param isCovered 是否覆盖原矩阵，如果为true，则在原矩阵上修改，否则返回新矩阵
     * @return 结果矩阵
     */
    default Matrix muti(double a,boolean isCovered){
        if(isCovered){
            Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().forEach(row->
                Stream.iterate(0,item->item+1).limit(getColLen()).forEach(col->
                        this.set(row,col,get(row,col)*a)
                )
            );
            return  this;
        }
        else{
            Matrix result = zero();
            Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().forEach(row->
                Stream.iterate(0,item->item+1).limit(getColLen()).forEach(col->
                        result.set(row,col,get(row,col)*a)
                )
            );
            return result;
        }
    }

    /**
     * 矩阵与向量相乘，返回结果向量
     * @param v 与矩阵相乘的向量
     * @return 结果向量
     */
    default Vec muti(Vec v) throws NotSupportException{
        if(!Optional.ofNullable(v).isPresent()||v.len()!=getColLen()){
            throw  new NotSupportException("The v length is not correct!");
        }
        Vec[] cols = new Vec[getColLen()];
        Stream.iterate(0,item->item+1).limit(getColLen()).parallel().forEach(col->
                cols[col]=getCol(col)
        );
        Vec result = v.zero(getRowLen());
        Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().forEach(row->{
            for(int j=0;j<cols.length;j++){
                    result.set(row,result.get(row)+cols[j].get(row)*v.get(j));
            }
        });
        return result;
    }

    /**
     * 矩阵与矩阵相乘，返回结果矩阵
     * @param other 相乘的矩阵
     * @return 结果矩阵
     */
    default Matrix muti(Matrix other) throws NotSupportException{
        if(!Optional.ofNullable(other).isPresent()||other.getRowLen()!=getColLen()){
            throw new NotSupportException("The length of other is not correct!");
        }
        Matrix result = zero(getRowLen(), other.getColLen());
        Stream.iterate(0,item->item+1).limit(result.getRowLen()).parallel().forEach(row->
            Stream.iterate(0,item->item+1).limit(result.getColLen()).parallel().forEach(col->{
                try {
                    result.set(row,col,getRow(row).innerProduct(other.getCol(col)));
                } catch (NotSupportException e) {ExceptionUtils.doCatch(e);}
            })
        );
        return result;
    }


    /**
     * 判断两个矩阵是否相等
     * @param other 比较的矩阵
     * @return 判断结果
     */
    default boolean isEqual(Matrix other) {
        if(!Optional.ofNullable(other).isPresent()||getRowLen()!=other.getRowLen()||getColLen()!=other.getColLen()){
            return false;
        }
        return Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().allMatch(row->{
            for(int j=0;j<getColLen();j++){
                    if(get(row,j)!=other.get(row,j)){
                        return false;
                    }
            }
            return true;
        });
    }

    /**
     * 矩阵相加，返回结果矩阵
     * @param other 相加的矩阵
     * @param isCovered 是否覆盖原来的矩阵，true表示覆盖，false表示返回新矩阵
     * @return 结果矩阵
     */
    default Matrix add(Matrix other,boolean isCovered) throws NotSupportException {
        if(!Optional.ofNullable(other).isPresent()||getRowLen()!=other.getRowLen()||getColLen()!=other.getColLen()){
            throw new NotSupportException("Other matrix must be the same shape of this!");
        }
        if(isCovered){
            Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().forEach(row->
                Stream.iterate(0,item->item+1).limit(getColLen()).parallel().forEach(col->
                        set(row,col,get(row,col)+other.get(row,col))
                )
            );
            return this;
        }
        else{
            Matrix result = zero();
            Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().forEach(row->
                Stream.iterate(0,item->item+1).limit(getColLen()).parallel().forEach(col->
                        result.set(row,col,get(row,col)+other.get(row,col))
                )
            );
            return result;
        }
    }

    /**
     * 判断是否方阵
     * @return 判断结果
     */
    default boolean isSquare() {
        return getColLen()==getRowLen();
    }

    /**
     * 是否稠密矩阵
     * @return 返回判断结果
     */
    boolean isDense();

    /**
     * 是否下三角矩阵
     * @return 返回判断结果
     */
    default boolean isLowerTri(){
        return Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().allMatch(row->{
            for(int j=row+1;j<getColLen();j++){
                    if(get(row,j)!=0){
                        return false;
                    }
            }
            return true;
        });
    }

    /**
     * 是否上三角矩阵
     * @return 返回判断结果
     */
    default boolean isUpperTri(){
        return Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().allMatch(row->{
            for(int j=0;j<row;j++){
                    if(get(row,j)!=0){
                        return false;
                    }
            }
            return true;
        });
    }

    /**
     * 是否Hessenberg矩阵
     * @return 返回判断结果
     */
    default boolean isHessenberg(){
        return Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().allMatch(row->{
            for(int j=0;j<row-1;j++){
                    if(get(row,j)!=0){
                        return false;
                    }
            }
            return true;
        });
    }

    /**
     * 判断是否正定
     * @return 返回判断结果
     */
    boolean isPositiveDefinite();

    /**
     * 判断是否对称
     * @return 返回判断结果
     */
    default boolean isSymmetric(){
        if(!isSquare()){return false;}
        return Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().allMatch(row->{
            for(int j=0;j<row;j++){
                    if(get(row,j)!=get(j,row)){
                        return false;
                    }
            }
            return true;
        });
    }

    /**
     * 判断是否三对角矩阵
     * @return  返回判断结果
     */
    default boolean isTriDiag(){
        if(!isSquare()){
            return false;
        }
        return Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().allMatch(row-> {
            for (int j = 0; j < row - 1;j++){
                    if(get(row,j)!=0){return false;}
            }
            for(int j=row+2;j<getColLen();j++){
                    if(get(row,j)!=0){return false;}
            }
            return true;
        });
    }


    /**
     * 交换矩阵的i，j两行
     * @param i 第i行
     * @param j 第j行
     */
    default void  exchangeRow(int i,int j) {
        Stream.iterate(0,item->item+1).limit(getColLen()).forEach(col->{
                double tmp = get(j,col);
                set(j,col,get(i,col));
                set(i,col,tmp);
        });
    }

    /**
     * 交换矩阵的i,j两列
     * @param i 第i列
     * @param j 第j列
     */
    default void  exchangeCol(int i,int j) {
        Stream.iterate(0,item->item+1).limit(getRowLen()).forEach(row-> {
                double tmp = get(row, j);
                set(row, j, get(row, i));
                set(row, i, tmp);
        });
    }

    /**
     * 计算矩阵的f-范数
     * @return 范数值
     */
    default double fnorm(){
        return Math.sqrt(Stream.iterate(0, item -> item + 1).limit(getRowLen()).mapToDouble(row -> (Double) Stream.iterate(0, item -> item + 1).limit(getColLen()).mapToDouble(col -> Math.pow(get(row, col), 2.0)).sum()).sum());
    }

    /**
     * 计算矩阵的OFF范数，最大“行无穷范数”
     * @return off值
     */
    default double off() {
        int maxRow =  Stream.iterate(0,item->item+1).limit(getRowLen()).parallel().max((row1,row2)->{
                Double sum1=Math.abs(get(row1,0));
                Double sum2 = Math.abs(get(row2,0));
                for(int j=1;j<getColLen();j++){
                    if(sum1<Math.abs(get(row1,j))){
                        sum1=Math.abs(get(row1,j));
                    }
                    if(sum2<Math.abs(get(row2,j))) {
                        sum2 = Math.abs(get(row2, j));
                    }
                }
            return sum1.compareTo(sum2);
        }).orElseGet(()-> Integer.MIN_VALUE);
            return Stream.iterate(0, item->item+1).limit(getColLen()).parallel().mapToDouble(col-> Math.abs(get(maxRow, col))).sum();
    }

    /**
     * 计算矩阵的1-范数,最大“列1范数”
     * @return 范数值
     */
    default double norm1(){
        int maxCol =  Stream.iterate(0,item->item+1).limit(getColLen()).parallel().max((col1,col2)->{
             Double sum1=0.0,sum2 = 0.0;
             for(int i=0;i<getRowLen();i++){
                     sum1+=Math.abs(get(i,col1));
                     sum2+=Math.abs(get(i,col2));
             }
             return sum1.compareTo(sum2);
        }).orElseGet(()-> Integer.MIN_VALUE);
        return Stream.iterate(0, item -> item + 1).limit(getRowLen()).parallel().mapToDouble(row -> Math.abs(get(row, maxCol))).sum();
    }

    /**
     * 计算矩阵的2-范数
     * 又称为最大谱模，指的是sqrt(lamda(A^T*A))
     * @return 范数值
     */
    double norm2();

    /**
     * 返回一个矩阵，元素为原矩阵的绝对值
     * @param isCovered 是否覆盖原矩阵，true为覆盖，false返回新矩阵
     * @return 返回的结果矩阵
     */
    default Matrix abs(boolean isCovered){
        if(isCovered){
            Stream.iterate(0,item->item+1).limit(getRowLen()).forEach(row->
                Stream.iterate(0,item->item+1).limit(getColLen()).forEach(col->
                        set(row,col,Math.abs(get(row,col)))
                )
            );
            return this;
        }
        else{
            Matrix result = zero();
            Stream.iterate(0,item->item+1).limit(getRowLen()).forEach(row->
                Stream.iterate(0,item->item+1).limit(getColLen()).forEach(col->
                        result.set(row,col,Math.abs(get(row,col)))
                )
            );
            return result;
        }
    }


    /**
     * 将当前矩阵转化成一个稀疏矩阵，返回
     * @return 转化后的矩阵
     */
    Matrix toSparse() throws NotSupportException;

    /**
     * 将矩阵转换成稠密矩阵返回
     * @return 转化后的矩阵
     */
    Matrix toDense() throws NotSupportException;

    /**
     *
     * @param k 第k条对角线，上对角线k>0，下对角线k<0
     * @return
     */
    default int diagLength(int k){
        int min = Math.min(getColLen(),getRowLen());
        /**
         * 行数大于列数，“长条形”矩阵
         */
        if(min == getColLen()){
            if(k<0){
                /**
                 * 长度仍旧和列宽一样
                 */
               if((-k)<=(getRowLen()-min)){
                   return min;
               }
               else{
                   return min-(-k-(getRowLen()-min));
               }
            }
            else{
                return min - k;
            }
        }
        /**
         * 列数大于行数，相当于“宽”矩阵
         */
        else{
            if(k>0){
                if(k<=(getColLen()-min)){
                    return min;
                }
                else{
                    return min-(k-(getColLen()-min));
                }
            }
            else{
                return min-k;
            }
        }
    }
}
